Set RNotebook for Root Directory

Set Working Directory

setwd("~/Desktop/af-werx")
The working directory was changed to /Users/datasociety/Desktop/af-werx inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.

Getting working Directory

getwd()
[1] "/Users/datasociety/Desktop/af-werx/data"

creating and setting the working directory

main_dir = "~/Desktop/af-werx"
data_dir = paste0(main_dir, "/data")
data_dir
[1] "~/Desktop/af-werx/data"
plot_dir = paste0(main_dir, "/plots")
plot_dir
[1] "~/Desktop/af-werx/plots"
setwd(data_dir)
getwd()
[1] "/Users/datasociety/Desktop/af-werx/data"

2 INTERACTIVE VISUALIZATION R

NOTE: To run individual pieces of code, select the line of code and

press ctrl + enter for PCs or command + enter for Macs

#=================================================- #### Slide 7: Load tidyverse and set up ggplot2 theme ####

# Load tidyverse library 
# (it includes ggplot2).
library(tidyverse)
# Save our custom `ggplot` theme to a variable.
my_ggtheme = theme_bw() +                     
  theme(axis.title = element_text(size = 20),
        axis.text = element_text(size = 16),
        legend.text = element_text(size = 16),              
        legend.title = element_text(size = 18),             
        plot.title = element_text(size = 25),               
        plot.subtitle = element_text(size = 18))    

#=================================================- #### Slide 8: Setup: load dataset for EDA ####

# Set working directory to where we store data.
setwd(data_dir)

# Read CSV file called 
#"ChemicalManufacturingProcess.csv".
CMP = 
  read.csv("ChemicalManufacturingProcess.csv",
           header = TRUE)
# View CMP dataset in the tabular data explorer.
View(CMP)

#=================================================- #### Slide 10: Setup: subset data with select ####

# Select variables from CMP data.
CMP_subset = select(CMP,                                           #<- set data
                    Yield:BiologicalMaterial03,                    #<- range of columns to select
                    ManufacturingProcess01:ManufacturingProcess03) #<- range of columns to select

# Inspect the first few observations in the subset.
head(CMP_subset)

#=================================================- #### Slide 11: Setup: wide to long data conversion with gather ####

CMP_subset_long = CMP_subset %>%
  gather(key = "variable",
         value = "value") 
# Inspect the first few observations.
head(CMP_subset_long)

# Inspect the last few observations.
tail(CMP_subset_long)

#=================================================- #### Slide 12: Setup: data cleaning with mutate ####

# Make names of processes and materials
# more user friendly and readable.
CMP_subset_long = CMP_subset_long %>%
  
     # Replace `Biological` with `Bio`.
     mutate(variable = 
              str_replace(variable,     #<- in column `variable`
                          "Biological", #<- replace "Biological"
                          "Bio ")) %>%  #<- with "Bio "
  
     # Replace `Manufacturing` with `Man.`.
     mutate(variable = 
              str_replace(variable, 
                          "Manufacturing", 
                          "Man. ")) %>%
  
     # Remove `0` from numbering.
     mutate(variable = 
              str_replace(variable, 
                          "0", 
                          " "))
# Inspect few first 
# entries in the data.
head(CMP_subset_long)

# Inspect few last 
# entries in the data.
tail(CMP_subset_long)

#=================================================- #### Slide 16: Set up & link data: make boxplots ####

# A basic boxplot with pre-saved theme.
boxplots = ggplot(CMP_subset_long,   #<- set the base plot + data
                  aes(x = variable,  #<- map `variable` to x-axis
                      y = value)) +  #<- map `value` to y-axis
           geom_boxplot() +          #<- add boxplot geom
           my_ggtheme                #<- add pre-saved theme

# View plot.
boxplots

#=================================================- #### Slide 17: Adjust: boxplot aesthetics ####

# Make color of fill based on variable.
boxplots = ggplot(CMP_subset_long,        
                  aes(x = variable, 
                      y = value, 
                      fill = variable)) + #<- add fill to aesthetics
  geom_boxplot() + 
  my_ggtheme

# View plot.
boxplots

#=================================================- #### Slide 18: Adjust & polish: boxplot legends ####


# Remove redundant legend.
boxplots = boxplots + 
  labs(title = "CMP data variables", #<- add title and subtitle
       subtitle = "Boxplot of unscaled data") +
  guides(fill = FALSE)               #<- remove fill legend

# View plot.
boxplots

#=================================================- #### Slide 20: Setup: normalize data with group_by + mutate ####

# Normalize the CMP data.
CMP_subset_long = CMP_subset_long %>%
  group_by(variable) %>%              #<- group values by variable
  mutate(norm_value =                 #<- make `norm_value` column
           value/max(value,           #<- divide value by group max
                     na.rm = TRUE))   #<- don't forget the NAs!

CMP_subset_long
NA

#=================================================- #### Slide 21: Setup: make base plot with normalized data ####


# Construct the base plot for normalized data.
base_norm_plot = ggplot(CMP_subset_long,        #<- set data
                        aes(x = variable,       #<- map `variable`
                            y = norm_value,     #<- map `norm_value`
                            fill = variable)) + #<- set fill
                 my_ggtheme                     #<- add theme

# View base plot + theme.
base_norm_plot

#=================================================- #### Slide 22: Adjust: normalized boxplot’s effects & legends ####

# Make color of fill based on variable.
boxplots_norm = base_norm_plot +     #<- set base plot   
                geom_boxplot() +     #<- add geom
                guides(fill = FALSE) #<- remove redundant legend 

# View updated plot.
boxplots_norm

#=================================================- #### Slide 23: Polish: normalized boxplot details ####

# Make outliers stand out with red color and bigger size.
boxplots_norm = boxplots_norm +       #<- previously saved plot
  geom_boxplot(outlier.color = "red", #<- adjust outlier color
               outlier.size = 5) +    #<- adjust outlier size
  labs(title = "CMP data variables",  #<- add title and subtitle
       subtitle = "Boxplot of scaled data")

# View updated plot.
boxplots_norm

#=================================================- #### Slide 25: Setup the density plot of normalized data ####

# Let's save base plot for density as well.
density_norm = ggplot(CMP_subset_long,   #<- set data
                        aes(x = norm_value, #<- map
                            fill = variable)) + #<- map fill
                 my_ggtheme  + #<- add theme
                geom_density(alpha = 0.3) #<- adjust fill
density_norm

#=================================================- #### Slide 26: Adjust the axes and visual effects of density plot ####

# Instead of overlaying densities, split them into individual plots called facets.
density_norm = density_norm +                        #<- previously saved plot
               facet_wrap (~ variable,               #<- make facets by variable
                           ncol = 4) +               #<- set a 4-column grid
              geom_vline(data = CMP_subset_long,     #<- set data
                          aes(xintercept =           #<- set x-intercept 
                                mean(norm_value,     #<- to mean
                                     na.rm = TRUE),  #<- handle NAs!  
                              color = variable),     #<- map color
                          linetype = "dashed",       #<- set line type 
                          size = 1.5)                #<- set line size

# View updated plot.
density_norm

#=================================================- #### Slide 27: Polish: legends of density plot ####

# Remove redundant legend.
density_norm = density_norm +           #<- previously saved plot
               guides(fill = FALSE,     #<- no legend for `fill` of density
                      color = FALSE) +  #<- no legend for `color` of line
              theme(strip.text.x = 
         element_text(size = 14)) +     #<- set strip text size
  labs(title = "CMP data variables",    #<- add title and subtitle
       subtitle = "Density of scaled data")
density_norm

#=================================================- #### Slide 29: Exercise 1 ####

still need to do

#=================================================- #### Slide 33: Setup: transform data for scatterplot ####

CMP_subset_long2 = CMP_subset %>%
  gather(BiologicalMaterial01:ManufacturingProcess03, #<- gather all variables but `Yield`
         key = "variable",                            #<- set key to `variable`
         value = "value") %>%                         #<- set value to `value`
  # All other transformations we've done before.
  mutate(variable = str_replace(variable, "Biological", "Bio ")) %>%
  mutate(variable = str_replace(variable, "Manufacturing", "Man. ")) %>%
  mutate(variable = str_replace(variable, "0", " ")) %>%
  group_by(variable) %>%
  mutate(norm_value = value/max(value, na.rm = TRUE))

# Inspect the data.
head(CMP_subset_long2)

#=================================================- #### Slide 34: Setup & link: normalized data base plot ####


# Create a base plot.
base_norm_plot = ggplot(data = CMP_subset_long2,  #<- set data 
                        aes(x = norm_value,       #<- set x-axis to represent normalized value
                            y = Yield,            #<- y-axis to represent `Yield` 
                            color = variable)) +  #<- set color to depend on `variable`
                        my_ggtheme                #<- set theme

#=================================================- #### Slide 35: Setup & adjust: normalized data scatterplot ####

# Create a scatterplot.
scatter_norm = base_norm_plot +        #<- base plot
               geom_point(size = 3,    #<- add point geom with size of point = 3
                          alpha = 0.7) #<- make it 70% opaque

# View updated plot.
scatter_norm

#=================================================- #### Slide 36: Adjust: add density geom to scatterplot ####

# Adjust scatterplot to include 2D density.
scatter_norm = scatter_norm +              #<- previously saved plot
               geom_density2d(alpha = 0.7) #<- add 2D density geom with 70% opaque color

# View updated plot.
scatter_norm

#=================================================- #### Slide 37: Adjust: wrap scatterplots in facets ####

# Wrap each scatterplot into a facet.
scatter_norm = scatter_norm +                   #<- previously saved plot
               facet_wrap(~ variable, ncol = 3) #<- wrap plots by variable into 3 columns

# View updated plot.
scatter_norm

#=================================================- #### Slide 38: Adjust & polish: legends and text in scatterplot ####

# Add finishing touches to the plot.
scatter_norm = scatter_norm +                        #<- previously saved plot
  guides(color = FALSE) +                            #<- remove legend for color mappings
  theme(strip.text.x = element_text(size = 14)) +    #<- increase text size in strips of facets
  labs(title = "CMP data: Yield vs. other variables",#<- add title and subtitle
       subtitle = "2D distribution of scaled data")

# View updated plot.
scatter_norm

#=================================================- #### Slide 40: Saving plots in R: PNG exported ####

Set working directory

to where we want to save our plots.

setwd(plot_dir)
The working directory was changed to /Users/datasociety/Desktop/af-werx/plots inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
png("CMP_boxplots_norm.png",
    width = 1200, 
    height = 600, 
    units = "px")
boxplots_norm
dev.off()
null device 
          1 
png("CMP_density_norm.png",
    width = 1200, 
    height = 600, 
    units = "px")
density_norm
dev.off()
null device 
          1 
png("CMP_scatterplot_norm.png",
    width = 1200, 
    height = 600, 
    units = "px")
scatter_norm
dev.off()
null device 
          1 

#=================================================- #### Slide 43: Saving plots in R: PDF exported ####

# Set working directory to where you want to save plots.
setwd(plot_dir)
The working directory was changed to /Users/datasociety/Desktop/af-werx/plots inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
pdf("CMP_plots.pdf", #<- name of file
    width = 16,      #<- width in inches
    height = 8)      #<- height ...

boxplots_norm        #<- plot 1
density_norm         #<- plot 2
scatter_norm         #<- plot 3

dev.off()            #<- clear graphics device
null device 
          1 

#=================================================- #### Slide 45: Exercise 2 ####

#=================================================- #### Slide 47: Interactive visualizations: highcharter ####

# Install `highcharter` package.
install.packages("highcharter")
also installing the dependencies ‘httpuv’, ‘xtable’, ‘sourcetools’, ‘later’, ‘promises’, ‘XML’, ‘data.table’, ‘TTR’, ‘shiny’, ‘htmlwidgets’, ‘rlist’, ‘zoo’, ‘xts’, ‘quantmod’, ‘igraph’, ‘crosstalk’

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/httpuv_1.5.1.tgz'
Content type 'application/x-gzip' length 2936016 bytes (2.8 MB)
==================================================
downloaded 2.8 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/xtable_1.8-4.tgz'
Content type 'application/x-gzip' length 702711 bytes (686 KB)
==================================================
downloaded 686 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/sourcetools_0.1.7.tgz'
Content type 'application/x-gzip' length 138829 bytes (135 KB)
==================================================
downloaded 135 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/later_0.8.0.tgz'
Content type 'application/x-gzip' length 385957 bytes (376 KB)
==================================================
downloaded 376 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/promises_1.0.1.tgz'
Content type 'application/x-gzip' length 307177 bytes (299 KB)
==================================================
downloaded 299 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/XML_3.98-1.20.tgz'
Content type 'application/x-gzip' length 2219480 bytes (2.1 MB)
==================================================
downloaded 2.1 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/data.table_1.12.2.tgz'
Content type 'application/x-gzip' length 1841719 bytes (1.8 MB)
==================================================
downloaded 1.8 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/TTR_0.23-4.tgz'
Content type 'application/x-gzip' length 521141 bytes (508 KB)
==================================================
downloaded 508 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/shiny_1.3.2.tgz'
Content type 'application/x-gzip' length 4655011 bytes (4.4 MB)
==================================================
downloaded 4.4 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/htmlwidgets_1.3.tgz'
Content type 'application/x-gzip' length 795270 bytes (776 KB)
==================================================
downloaded 776 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/rlist_0.4.6.1.tgz'
Content type 'application/x-gzip' length 239321 bytes (233 KB)
==================================================
downloaded 233 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/zoo_1.8-6.tgz'
Content type 'application/x-gzip' length 1089387 bytes (1.0 MB)
==================================================
downloaded 1.0 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/xts_0.11-2.tgz'
Content type 'application/x-gzip' length 950614 bytes (928 KB)
==================================================
downloaded 928 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/quantmod_0.4-15.tgz'
Content type 'application/x-gzip' length 962931 bytes (940 KB)
==================================================
downloaded 940 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/igraph_1.2.4.1.tgz'
Content type 'application/x-gzip' length 7681590 bytes (7.3 MB)
==================================================
downloaded 7.3 MB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/crosstalk_1.0.0.tgz'
Content type 'application/x-gzip' length 662863 bytes (647 KB)
==================================================
downloaded 647 KB

trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/highcharter_0.7.0.tgz'
Content type 'application/x-gzip' length 2970318 bytes (2.8 MB)
==================================================
downloaded 2.8 MB

The downloaded binary packages are in
    /var/folders/pz/0fvcbrdd2hb75sqqgw0q7thw0000gn/T//RtmpADFSOg/downloaded_packages
# Load the library.
library(highcharter)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'xts':
  method     from
  as.zoo.xts zoo 
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Highcharts (www.highcharts.com) is a Highsoft software product which is
not free for commercial and Governmental use
# View documentation.
library(help = "highcharter")

#=================================================- #### Slide 55: Highcharts generic function hchart: scatter ####


# Construct an interactive scatterplot.
scatter_interactive =              #<- name the plot   
  hchart(CMP_subset_long2,         #<- set data
         "scatter",                #<- make type "scatter"
          hcaes(x = norm_value,    #<- map x-axis
                y = Yield,         #<- map y-axis
                group = variable)) #<- group by

#=================================================- #### Slide 56: Highcharts generic function hchart: scatter ####

scatter_interactive

#=================================================- #### Slide 58: Highcharts generic function hchart: scatter ####

# Pipe chart options to original chart.
scatter_interactive = scatter_interactive %>%
  # Use chart options to specify zoom.
  hc_chart(zoomType = "xy") 

scatter_interactive

#=================================================- #### Slide 59: Highcharts generic function hchart: scatter ####

# Pipe chart options to original chart.
scatter_interactive = scatter_interactive %>%
 # Add title to your plot.
 hc_title(text = "CMP data: Yield vs. other variables")

scatter_interactive

#=================================================- #### Slide 60: Correlation matrix with hchart ####

# Compute a correlation matrix for the first 
# 4 variables in our data.
cor_matrix = cor(CMP_subset[, 1:4])
# Construct a correlation plot by 
# simply giving the plotting function
# a correlation matrix.
correlation_interactive = hchart(cor_matrix) %>%
 # Add title to your plot.
 hc_title(text = "CMP data: correlation")

correlation_interactive

#=================================================- #### Slide 62: Exercise 3 ####

#=================================================- #### Slide 65: Summary column plot with hchart ####

# Create data summary.
CMP_summary = summary(CMP_subset)

# Save it as a dataframe.
CMP_summary = as.data.frame(CMP_summary)

# Inspect the data.
head(CMP_summary)
# Remove an empty variable.
CMP_summary$Var1 = NULL

# Rename remaining columns.
colnames(CMP_summary) = c("Variable", 
                          "Summary")
# Inspect updated data.
head(CMP_summary)

#=================================================- #### Slide 66: Summary column plot with hchart ####

# Separate `Summary` column into 2 columns.
CMP_summary = CMP_summary %>%              #<- set original data 
  separate(Summary,                        #<- separate `Summary` variable 
           into = c("Statistic", "Value"), #<- into 2 columns: `Statistic`, `Value`
           sep = ":",                      #<- set separating character
           convert = TRUE)                 #<- where applicable convert data (to numeric)
# Inspect the first few entries in the data.
head(CMP_summary)

# Inspect total number of rows in data including NAs.
nrow(CMP_summary)
[1] 49

#=================================================- #### Slide 67: Summary column plot with hchart ####

# Inspect `Value` column for `NAs`.
which(is.na(CMP_summary$Value) == TRUE)
[1]  7 14 21 28
# Subset only rows where `Value` is not NAs.
CMP_summary = subset(CMP_summary, !is.na(Value))
# Now the number of rows should be 4 less.
nrow(CMP_summary)
[1] 45
# Construct the summary chart.
CMP_summary_interactive = 
  hchart(CMP_summary,             #<- set data
         "column",                #<- set type (`column` in highcharts)
         hcaes(x = Statistic,     #<- arrange `Statistics` across x-axis
               y = Value,         #<- map `Value` of each `Statistic` to y-axis
               group = Variable)) #<- group columns by `Variable`

#=================================================- #### Slide 68: Summary column plot with hchart ####

CMP_summary_interactive

#=================================================- #### Slide 69: Summary column plot with hchart ####

# Adjust tooltip options by piping `hc_tooltip` to base plot.
CMP_summary_interactive = CMP_summary_interactive %>% 
  hc_tooltip(shared = TRUE)  %>%               #<- `shared` needs to be set to `TRUE`
  hc_title(text = "CMP data variable summary") #<- add title to your plot

#=================================================- #### Slide 70: Summary column plot with hchart ####

CMP_summary_interactive

#=================================================- #### Slide 73: Highcharts boxplot: hcboxplot ####

#=================================================- #### Slide 76: Highcharts boxplot: hcboxplot ####

# Enhance original boxplot with some options.
boxplot_interactive = boxplot_interactive %>% 
  hc_plotOptions(       #<- plot options
    boxplot = list(     #<- for boxplot 
      colorByPoint = TRUE)) #<- color each box
boxplot_interactive

#=================================================- #### Slide 79: Compound plots: density + lines example ####

layered_density_interactive = highchart() %>% #main chart
  hc_chart(type = "area") %>% #global chart option for to apply for all layers
  hc_add_series(data = density(CMP_subset$Yield),
                name = "Yield") %>%
  hc_add_series(data = density(CMP_subset$BiologicalMaterial01),
                name = "Bio Material 1") %>%
  hc_add_series(data = density(CMP_subset$ManufacturingProcess01, na.rm = TRUE),
                name = "Man. Process 1") %>%
  hc_xAxis(plotLines = list(
            list(label = list(text = "Yield avg."),
                  width = 2,
                  color = "red",
                  value = mean(CMP_subset$Yield)),
            list(label = list(text = "Bio Material 1 avg."),
                  width = 2,
                  color = "red",
                  value = mean(CMP_subset$BiologicalMaterial01)),
            list(label = list(text = "Man. Process 1 avg."),
                  width = 2,
                  color = "red",
                  value = mean(CMP_subset$ManufacturingProcess01, na.rm = TRUE)))) %>%
  hc_tooltip(crosshairs = TRUE) %>%
  hc_title(text = "CMP data: density and average of select variables")

#=================================================- #### Slide 80: Compound plots: highchart with layers ####

layered_density_interactive

#=================================================- #### Slide 83: Exercise 4 ####

CONGRATULATIONS ON COMPLETING THIS MODULE!

LS0tCnRpdGxlOiAiSW50cm8gdG8gUl9ENiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIFNldCBSTm90ZWJvb2sgZm9yIFJvb3QgRGlyZWN0b3J5CmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBlY2hvPUZBTFNFfQpyZXF1aXJlKCJrbml0ciIpCm9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAifi9EZXNrdG9wL2FmLXdlcngvZGF0YSIpCmBgYAoKCiMgU2V0IFdvcmtpbmcgRGlyZWN0b3J5CmBgYHtyfQpzZXR3ZCgifi9EZXNrdG9wL2FmLXdlcngiKQpgYGAKCiMgR2V0dGluZyB3b3JraW5nIERpcmVjdG9yeQpgYGB7cn0KZ2V0d2QoKQpgYGAKCiMgY3JlYXRpbmcgYW5kIHNldHRpbmcgdGhlIHdvcmtpbmcgZGlyZWN0b3J5CmBgYHtyfQptYWluX2RpciA9ICJ+L0Rlc2t0b3AvYWYtd2VyeCIKZGF0YV9kaXIgPSBwYXN0ZTAobWFpbl9kaXIsICIvZGF0YSIpCmRhdGFfZGlyCnBsb3RfZGlyID0gcGFzdGUwKG1haW5fZGlyLCAiL3Bsb3RzIikKcGxvdF9kaXIKc2V0d2QoZGF0YV9kaXIpCmdldHdkKCkKYGBgCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyMjIyMjICAgIENPUFlSSUdIVCAtIERBVEEgU09DSUVUWSAgICMjIyMjIyMjIyMjIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMjIDIgSU5URVJBQ1RJVkUgVklTVUFMSVpBVElPTiBSICMjCgojIyBOT1RFOiBUbyBydW4gaW5kaXZpZHVhbCBwaWVjZXMgb2YgY29kZSwgc2VsZWN0IHRoZSBsaW5lIG9mIGNvZGUgYW5kCiMjICAgICAgIHByZXNzIGN0cmwgKyBlbnRlciBmb3IgUENzIG9yIGNvbW1hbmQgKyBlbnRlciBmb3IgTWFjcwoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDc6IExvYWQgdGlkeXZlcnNlIGFuZCBzZXQgdXAgZ2dwbG90MiB0aGVtZSAgIyMjIwoKYGBge3J9CiMgTG9hZCB0aWR5dmVyc2UgbGlicmFyeSAKIyAoaXQgaW5jbHVkZXMgZ2dwbG90MikuCmxpYnJhcnkodGlkeXZlcnNlKQojIFNhdmUgb3VyIGN1c3RvbSBgZ2dwbG90YCB0aGVtZSB0byBhIHZhcmlhYmxlLgpteV9nZ3RoZW1lID0gdGhlbWVfYncoKSArICAgICAgICAgICAgICAgICAgICAgCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLAogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksICAgICAgICAgICAgICAKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgICAgICAgICAgICAgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjUpLCAgICAgICAgICAgICAgIAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgICAgCmBgYAoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDg6IFNldHVwOiBsb2FkIGRhdGFzZXQgZm9yIEVEQSAgIyMjIwoKCmBgYHtyfQojIFNldCB3b3JraW5nIGRpcmVjdG9yeSB0byB3aGVyZSB3ZSBzdG9yZSBkYXRhLgpzZXR3ZChkYXRhX2RpcikKCiMgUmVhZCBDU1YgZmlsZSBjYWxsZWQgCiMiQ2hlbWljYWxNYW51ZmFjdHVyaW5nUHJvY2Vzcy5jc3YiLgpDTVAgPSAKICByZWFkLmNzdigiQ2hlbWljYWxNYW51ZmFjdHVyaW5nUHJvY2Vzcy5jc3YiLAogICAgICAgICAgIGhlYWRlciA9IFRSVUUpCiMgVmlldyBDTVAgZGF0YXNldCBpbiB0aGUgdGFidWxhciBkYXRhIGV4cGxvcmVyLgpWaWV3KENNUCkKYGBgCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgMTA6IFNldHVwOiBzdWJzZXQgZGF0YSB3aXRoIHNlbGVjdCAgIyMjIwoKCmBgYHtyfQojIFNlbGVjdCB2YXJpYWJsZXMgZnJvbSBDTVAgZGF0YS4KQ01QX3N1YnNldCA9IHNlbGVjdChDTVAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICM8LSBzZXQgZGF0YQogICAgICAgICAgICAgICAgICAgIFlpZWxkOkJpb2xvZ2ljYWxNYXRlcmlhbDAzLCAgICAgICAgICAgICAgICAgICAgIzwtIHJhbmdlIG9mIGNvbHVtbnMgdG8gc2VsZWN0CiAgICAgICAgICAgICAgICAgICAgTWFudWZhY3R1cmluZ1Byb2Nlc3MwMTpNYW51ZmFjdHVyaW5nUHJvY2VzczAzKSAjPC0gcmFuZ2Ugb2YgY29sdW1ucyB0byBzZWxlY3QKCiMgSW5zcGVjdCB0aGUgZmlyc3QgZmV3IG9ic2VydmF0aW9ucyBpbiB0aGUgc3Vic2V0LgpoZWFkKENNUF9zdWJzZXQpCmBgYAoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDExOiBTZXR1cDogd2lkZSB0byBsb25nIGRhdGEgY29udmVyc2lvbiB3aXRoIGdhdGhlciAgIyMjIwpgYGB7cn0KQ01QX3N1YnNldF9sb25nID0gQ01QX3N1YnNldCAlPiUKICBnYXRoZXIoa2V5ID0gInZhcmlhYmxlIiwKICAgICAgICAgdmFsdWUgPSAidmFsdWUiKSAKIyBJbnNwZWN0IHRoZSBmaXJzdCBmZXcgb2JzZXJ2YXRpb25zLgpoZWFkKENNUF9zdWJzZXRfbG9uZykKCiMgSW5zcGVjdCB0aGUgbGFzdCBmZXcgb2JzZXJ2YXRpb25zLgp0YWlsKENNUF9zdWJzZXRfbG9uZykKYGBgCgoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSAxMjogU2V0dXA6IGRhdGEgY2xlYW5pbmcgd2l0aCBtdXRhdGUgICMjIyMKCmBgYHtyfQojIE1ha2UgbmFtZXMgb2YgcHJvY2Vzc2VzIGFuZCBtYXRlcmlhbHMKIyBtb3JlIHVzZXIgZnJpZW5kbHkgYW5kIHJlYWRhYmxlLgpDTVBfc3Vic2V0X2xvbmcgPSBDTVBfc3Vic2V0X2xvbmcgJT4lCiAgCiAgICAgIyBSZXBsYWNlIGBCaW9sb2dpY2FsYCB3aXRoIGBCaW9gLgogICAgIG11dGF0ZSh2YXJpYWJsZSA9IAogICAgICAgICAgICAgIHN0cl9yZXBsYWNlKHZhcmlhYmxlLCAgICAgIzwtIGluIGNvbHVtbiBgdmFyaWFibGVgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkJpb2xvZ2ljYWwiLCAjPC0gcmVwbGFjZSAiQmlvbG9naWNhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAiQmlvICIpKSAlPiUgICM8LSB3aXRoICJCaW8gIgogIAogICAgICMgUmVwbGFjZSBgTWFudWZhY3R1cmluZ2Agd2l0aCBgTWFuLmAuCiAgICAgbXV0YXRlKHZhcmlhYmxlID0gCiAgICAgICAgICAgICAgc3RyX3JlcGxhY2UodmFyaWFibGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJNYW51ZmFjdHVyaW5nIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIk1hbi4gIikpICU+JQogIAogICAgICMgUmVtb3ZlIGAwYCBmcm9tIG51bWJlcmluZy4KICAgICBtdXRhdGUodmFyaWFibGUgPSAKICAgICAgICAgICAgICBzdHJfcmVwbGFjZSh2YXJpYWJsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIjAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiICIpKQojIEluc3BlY3QgZmV3IGZpcnN0IAojIGVudHJpZXMgaW4gdGhlIGRhdGEuCmhlYWQoQ01QX3N1YnNldF9sb25nKQoKIyBJbnNwZWN0IGZldyBsYXN0IAojIGVudHJpZXMgaW4gdGhlIGRhdGEuCnRhaWwoQ01QX3N1YnNldF9sb25nKQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDE2OiBTZXQgdXAgJiBsaW5rIGRhdGE6IG1ha2UgYm94cGxvdHMgICMjIyMKCmBgYHtyfQojIEEgYmFzaWMgYm94cGxvdCB3aXRoIHByZS1zYXZlZCB0aGVtZS4KYm94cGxvdHMgPSBnZ3Bsb3QoQ01QX3N1YnNldF9sb25nLCAgICM8LSBzZXQgdGhlIGJhc2UgcGxvdCArIGRhdGEKICAgICAgICAgICAgICAgICAgYWVzKHggPSB2YXJpYWJsZSwgICM8LSBtYXAgYHZhcmlhYmxlYCB0byB4LWF4aXMKICAgICAgICAgICAgICAgICAgICAgIHkgPSB2YWx1ZSkpICsgICM8LSBtYXAgYHZhbHVlYCB0byB5LWF4aXMKICAgICAgICAgICBnZW9tX2JveHBsb3QoKSArICAgICAgICAgICM8LSBhZGQgYm94cGxvdCBnZW9tCiAgICAgICAgICAgbXlfZ2d0aGVtZSAgICAgICAgICAgICAgICAjPC0gYWRkIHByZS1zYXZlZCB0aGVtZQoKIyBWaWV3IHBsb3QuCmJveHBsb3RzCmBgYAoKCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgMTc6IEFkanVzdDogYm94cGxvdCBhZXN0aGV0aWNzICAjIyMjCmBgYHtyfQojIE1ha2UgY29sb3Igb2YgZmlsbCBiYXNlZCBvbiB2YXJpYWJsZS4KYm94cGxvdHMgPSBnZ3Bsb3QoQ01QX3N1YnNldF9sb25nLCAgICAgICAgCiAgICAgICAgICAgICAgICAgIGFlcyh4ID0gdmFyaWFibGUsIAogICAgICAgICAgICAgICAgICAgICAgeSA9IHZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSB2YXJpYWJsZSkpICsgIzwtIGFkZCBmaWxsIHRvIGFlc3RoZXRpY3MKICBnZW9tX2JveHBsb3QoKSArIAogIG15X2dndGhlbWUKCiMgVmlldyBwbG90Lgpib3hwbG90cwpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDE4OiBBZGp1c3QgJiBwb2xpc2g6IGJveHBsb3QgbGVnZW5kcyAgIyMjIwoKYGBge3J9CgojIFJlbW92ZSByZWR1bmRhbnQgbGVnZW5kLgpib3hwbG90cyA9IGJveHBsb3RzICsgCiAgbGFicyh0aXRsZSA9ICJDTVAgZGF0YSB2YXJpYWJsZXMiLCAjPC0gYWRkIHRpdGxlIGFuZCBzdWJ0aXRsZQogICAgICAgc3VidGl0bGUgPSAiQm94cGxvdCBvZiB1bnNjYWxlZCBkYXRhIikgKwogIGd1aWRlcyhmaWxsID0gRkFMU0UpICAgICAgICAgICAgICAgIzwtIHJlbW92ZSBmaWxsIGxlZ2VuZAoKIyBWaWV3IHBsb3QuCmJveHBsb3RzCmBgYAoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSAyMDogU2V0dXA6IG5vcm1hbGl6ZSBkYXRhIHdpdGggZ3JvdXBfYnkgKyBtdXRhdGUgICMjIyMKCmBgYHtyfQojIE5vcm1hbGl6ZSB0aGUgQ01QIGRhdGEuCkNNUF9zdWJzZXRfbG9uZyA9IENNUF9zdWJzZXRfbG9uZyAlPiUKICBncm91cF9ieSh2YXJpYWJsZSkgJT4lICAgICAgICAgICAgICAjPC0gZ3JvdXAgdmFsdWVzIGJ5IHZhcmlhYmxlCiAgbXV0YXRlKG5vcm1fdmFsdWUgPSAgICAgICAgICAgICAgICAgIzwtIG1ha2UgYG5vcm1fdmFsdWVgIGNvbHVtbgogICAgICAgICAgIHZhbHVlL21heCh2YWx1ZSwgICAgICAgICAgICM8LSBkaXZpZGUgdmFsdWUgYnkgZ3JvdXAgbWF4CiAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpICAgIzwtIGRvbid0IGZvcmdldCB0aGUgTkFzIQoKQ01QX3N1YnNldF9sb25nCgpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDIxOiBTZXR1cDogbWFrZSBiYXNlIHBsb3Qgd2l0aCBub3JtYWxpemVkIGRhdGEgICMjIyMKCmBgYHtyfQoKIyBDb25zdHJ1Y3QgdGhlIGJhc2UgcGxvdCBmb3Igbm9ybWFsaXplZCBkYXRhLgpiYXNlX25vcm1fcGxvdCA9IGdncGxvdChDTVBfc3Vic2V0X2xvbmcsICAgICAgICAjPC0gc2V0IGRhdGEKICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSB2YXJpYWJsZSwgICAgICAgIzwtIG1hcCBgdmFyaWFibGVgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbm9ybV92YWx1ZSwgICAgICM8LSBtYXAgYG5vcm1fdmFsdWVgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpKSArICM8LSBzZXQgZmlsbAogICAgICAgICAgICAgICAgIG15X2dndGhlbWUgICAgICAgICAgICAgICAgICAgICAjPC0gYWRkIHRoZW1lCgojIFZpZXcgYmFzZSBwbG90ICsgdGhlbWUuCmJhc2Vfbm9ybV9wbG90CmBgYAoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSAyMjogQWRqdXN0OiBub3JtYWxpemVkIGJveHBsb3QncyBlZmZlY3RzICYgbGVnZW5kcyAgIyMjIwoKYGBge3J9CiMgTWFrZSBjb2xvciBvZiBmaWxsIGJhc2VkIG9uIHZhcmlhYmxlLgpib3hwbG90c19ub3JtID0gYmFzZV9ub3JtX3Bsb3QgKyAgICAgIzwtIHNldCBiYXNlIHBsb3QgICAKICAgICAgICAgICAgICAgIGdlb21fYm94cGxvdCgpICsgICAgICM8LSBhZGQgZ2VvbQogICAgICAgICAgICAgICAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgIzwtIHJlbW92ZSByZWR1bmRhbnQgbGVnZW5kIAoKIyBWaWV3IHVwZGF0ZWQgcGxvdC4KYm94cGxvdHNfbm9ybQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDIzOiBQb2xpc2g6IG5vcm1hbGl6ZWQgYm94cGxvdCBkZXRhaWxzICAjIyMjCgpgYGB7cn0KIyBNYWtlIG91dGxpZXJzIHN0YW5kIG91dCB3aXRoIHJlZCBjb2xvciBhbmQgYmlnZ2VyIHNpemUuCmJveHBsb3RzX25vcm0gPSBib3hwbG90c19ub3JtICsgICAgICAgIzwtIHByZXZpb3VzbHkgc2F2ZWQgcGxvdAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gInJlZCIsICM8LSBhZGp1c3Qgb3V0bGllciBjb2xvcgogICAgICAgICAgICAgICBvdXRsaWVyLnNpemUgPSA1KSArICAgICM8LSBhZGp1c3Qgb3V0bGllciBzaXplCiAgbGFicyh0aXRsZSA9ICJDTVAgZGF0YSB2YXJpYWJsZXMiLCAgIzwtIGFkZCB0aXRsZSBhbmQgc3VidGl0bGUKICAgICAgIHN1YnRpdGxlID0gIkJveHBsb3Qgb2Ygc2NhbGVkIGRhdGEiKQoKIyBWaWV3IHVwZGF0ZWQgcGxvdC4KYm94cGxvdHNfbm9ybQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDI1OiBTZXR1cCB0aGUgZGVuc2l0eSBwbG90IG9mIG5vcm1hbGl6ZWQgZGF0YSAgICMjIyMKYGBge3J9CiMgTGV0J3Mgc2F2ZSBiYXNlIHBsb3QgZm9yIGRlbnNpdHkgYXMgd2VsbC4KZGVuc2l0eV9ub3JtID0gZ2dwbG90KENNUF9zdWJzZXRfbG9uZywgICAjPC0gc2V0IGRhdGEKICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBub3JtX3ZhbHVlLCAjPC0gbWFwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpKSArICM8LSBtYXAgZmlsbAogICAgICAgICAgICAgICAgIG15X2dndGhlbWUgICsgIzwtIGFkZCB0aGVtZQogICAgICAgICAgICAgICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4zKSAjPC0gYWRqdXN0IGZpbGwKZGVuc2l0eV9ub3JtCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgMjY6IEFkanVzdCB0aGUgYXhlcyBhbmQgdmlzdWFsIGVmZmVjdHMgb2YgZGVuc2l0eSBwbG90ICAjIyMjCgpgYGB7cn0KIyBJbnN0ZWFkIG9mIG92ZXJsYXlpbmcgZGVuc2l0aWVzLCBzcGxpdCB0aGVtIGludG8gaW5kaXZpZHVhbCBwbG90cyBjYWxsZWQgZmFjZXRzLgpkZW5zaXR5X25vcm0gPSBkZW5zaXR5X25vcm0gKyAgICAgICAgICAgICAgICAgICAgICAgICM8LSBwcmV2aW91c2x5IHNhdmVkIHBsb3QKICAgICAgICAgICAgICAgZmFjZXRfd3JhcCAofiB2YXJpYWJsZSwgICAgICAgICAgICAgICAjPC0gbWFrZSBmYWNldHMgYnkgdmFyaWFibGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDQpICsgICAgICAgICAgICAgICAjPC0gc2V0IGEgNC1jb2x1bW4gZ3JpZAogICAgICAgICAgICAgIGdlb21fdmxpbmUoZGF0YSA9IENNUF9zdWJzZXRfbG9uZywgICAgICM8LSBzZXQgZGF0YQogICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4aW50ZXJjZXB0ID0gICAgICAgICAgICM8LSBzZXQgeC1pbnRlcmNlcHQgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihub3JtX3ZhbHVlLCAgICAgIzwtIHRvIG1lYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSksICAjPC0gaGFuZGxlIE5BcyEgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHZhcmlhYmxlKSwgICAgICM8LSBtYXAgY29sb3IKICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCAgICAgICAjPC0gc2V0IGxpbmUgdHlwZSAKICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMS41KSAgICAgICAgICAgICAgICAjPC0gc2V0IGxpbmUgc2l6ZQoKIyBWaWV3IHVwZGF0ZWQgcGxvdC4KZGVuc2l0eV9ub3JtCmBgYAoKCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgMjc6IFBvbGlzaDogbGVnZW5kcyBvZiBkZW5zaXR5IHBsb3QgICMjIyMKCmBgYHtyfQojIFJlbW92ZSByZWR1bmRhbnQgbGVnZW5kLgpkZW5zaXR5X25vcm0gPSBkZW5zaXR5X25vcm0gKyAgICAgICAgICAgIzwtIHByZXZpb3VzbHkgc2F2ZWQgcGxvdAogICAgICAgICAgICAgICBndWlkZXMoZmlsbCA9IEZBTFNFLCAgICAgIzwtIG5vIGxlZ2VuZCBmb3IgYGZpbGxgIG9mIGRlbnNpdHkKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gRkFMU0UpICsgICM8LSBubyBsZWdlbmQgZm9yIGBjb2xvcmAgb2YgbGluZQogICAgICAgICAgICAgIHRoZW1lKHN0cmlwLnRleHQueCA9IAogICAgICAgICBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkgKyAgICAgIzwtIHNldCBzdHJpcCB0ZXh0IHNpemUKICBsYWJzKHRpdGxlID0gIkNNUCBkYXRhIHZhcmlhYmxlcyIsICAgICM8LSBhZGQgdGl0bGUgYW5kIHN1YnRpdGxlCiAgICAgICBzdWJ0aXRsZSA9ICJEZW5zaXR5IG9mIHNjYWxlZCBkYXRhIikKZGVuc2l0eV9ub3JtCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgMjk6IEV4ZXJjaXNlIDEgICMjIyMKCgpzdGlsbCBuZWVkIHRvIGRvCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSAzMzogU2V0dXA6IHRyYW5zZm9ybSBkYXRhIGZvciBzY2F0dGVycGxvdCAgIyMjIwoKYGBge3J9CkNNUF9zdWJzZXRfbG9uZzIgPSBDTVBfc3Vic2V0ICU+JQogIGdhdGhlcihCaW9sb2dpY2FsTWF0ZXJpYWwwMTpNYW51ZmFjdHVyaW5nUHJvY2VzczAzLCAjPC0gZ2F0aGVyIGFsbCB2YXJpYWJsZXMgYnV0IGBZaWVsZGAKICAgICAgICAga2V5ID0gInZhcmlhYmxlIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIzwtIHNldCBrZXkgdG8gYHZhcmlhYmxlYAogICAgICAgICB2YWx1ZSA9ICJ2YWx1ZSIpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAjPC0gc2V0IHZhbHVlIHRvIGB2YWx1ZWAKICAjIEFsbCBvdGhlciB0cmFuc2Zvcm1hdGlvbnMgd2UndmUgZG9uZSBiZWZvcmUuCiAgbXV0YXRlKHZhcmlhYmxlID0gc3RyX3JlcGxhY2UodmFyaWFibGUsICJCaW9sb2dpY2FsIiwgIkJpbyAiKSkgJT4lCiAgbXV0YXRlKHZhcmlhYmxlID0gc3RyX3JlcGxhY2UodmFyaWFibGUsICJNYW51ZmFjdHVyaW5nIiwgIk1hbi4gIikpICU+JQogIG11dGF0ZSh2YXJpYWJsZSA9IHN0cl9yZXBsYWNlKHZhcmlhYmxlLCAiMCIsICIgIikpICU+JQogIGdyb3VwX2J5KHZhcmlhYmxlKSAlPiUKICBtdXRhdGUobm9ybV92YWx1ZSA9IHZhbHVlL21heCh2YWx1ZSwgbmEucm0gPSBUUlVFKSkKCiMgSW5zcGVjdCB0aGUgZGF0YS4KaGVhZChDTVBfc3Vic2V0X2xvbmcyKQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDM0OiBTZXR1cCAmIGxpbms6IG5vcm1hbGl6ZWQgZGF0YSBiYXNlIHBsb3QgICMjIyMKCmBgYHtyfQoKIyBDcmVhdGUgYSBiYXNlIHBsb3QuCmJhc2Vfbm9ybV9wbG90ID0gZ2dwbG90KGRhdGEgPSBDTVBfc3Vic2V0X2xvbmcyLCAgIzwtIHNldCBkYXRhIAogICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IG5vcm1fdmFsdWUsICAgICAgICM8LSBzZXQgeC1heGlzIHRvIHJlcHJlc2VudCBub3JtYWxpemVkIHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gWWllbGQsICAgICAgICAgICAgIzwtIHktYXhpcyB0byByZXByZXNlbnQgYFlpZWxkYCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdmFyaWFibGUpKSArICAjPC0gc2V0IGNvbG9yIHRvIGRlcGVuZCBvbiBgdmFyaWFibGVgCiAgICAgICAgICAgICAgICAgICAgICAgIG15X2dndGhlbWUgICAgICAgICAgICAgICAgIzwtIHNldCB0aGVtZQpgYGAKCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgMzU6IFNldHVwICYgYWRqdXN0OiBub3JtYWxpemVkIGRhdGEgc2NhdHRlcnBsb3QgICMjIyMKCmBgYHtyfQojIENyZWF0ZSBhIHNjYXR0ZXJwbG90LgpzY2F0dGVyX25vcm0gPSBiYXNlX25vcm1fcGxvdCArICAgICAgICAjPC0gYmFzZSBwbG90CiAgICAgICAgICAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMsICAgICM8LSBhZGQgcG9pbnQgZ2VvbSB3aXRoIHNpemUgb2YgcG9pbnQgPSAzCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcpICM8LSBtYWtlIGl0IDcwJSBvcGFxdWUKCiMgVmlldyB1cGRhdGVkIHBsb3QuCnNjYXR0ZXJfbm9ybQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDM2OiBBZGp1c3Q6IGFkZCBkZW5zaXR5IGdlb20gdG8gc2NhdHRlcnBsb3QgICMjIyMKCgpgYGB7cn0KIyBBZGp1c3Qgc2NhdHRlcnBsb3QgdG8gaW5jbHVkZSAyRCBkZW5zaXR5LgpzY2F0dGVyX25vcm0gPSBzY2F0dGVyX25vcm0gKyAgICAgICAgICAgICAgIzwtIHByZXZpb3VzbHkgc2F2ZWQgcGxvdAogICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkyZChhbHBoYSA9IDAuNykgIzwtIGFkZCAyRCBkZW5zaXR5IGdlb20gd2l0aCA3MCUgb3BhcXVlIGNvbG9yCgojIFZpZXcgdXBkYXRlZCBwbG90LgpzY2F0dGVyX25vcm0KYGBgCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDM3OiBBZGp1c3Q6IHdyYXAgc2NhdHRlcnBsb3RzIGluIGZhY2V0cyAgIyMjIwoKYGBge3J9CiMgV3JhcCBlYWNoIHNjYXR0ZXJwbG90IGludG8gYSBmYWNldC4Kc2NhdHRlcl9ub3JtID0gc2NhdHRlcl9ub3JtICsgICAgICAgICAgICAgICAgICAgIzwtIHByZXZpb3VzbHkgc2F2ZWQgcGxvdAogICAgICAgICAgICAgICBmYWNldF93cmFwKH4gdmFyaWFibGUsIG5jb2wgPSAzKSAjPC0gd3JhcCBwbG90cyBieSB2YXJpYWJsZSBpbnRvIDMgY29sdW1ucwoKIyBWaWV3IHVwZGF0ZWQgcGxvdC4Kc2NhdHRlcl9ub3JtCmBgYAoKCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgMzg6IEFkanVzdCAmIHBvbGlzaDogbGVnZW5kcyBhbmQgdGV4dCBpbiBzY2F0dGVycGxvdCAgIyMjIwoKYGBge3J9CiMgQWRkIGZpbmlzaGluZyB0b3VjaGVzIHRvIHRoZSBwbG90LgpzY2F0dGVyX25vcm0gPSBzY2F0dGVyX25vcm0gKyAgICAgICAgICAgICAgICAgICAgICAgICM8LSBwcmV2aW91c2x5IHNhdmVkIHBsb3QKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAjPC0gcmVtb3ZlIGxlZ2VuZCBmb3IgY29sb3IgbWFwcGluZ3MKICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkgKyAgICAjPC0gaW5jcmVhc2UgdGV4dCBzaXplIGluIHN0cmlwcyBvZiBmYWNldHMKICBsYWJzKHRpdGxlID0gIkNNUCBkYXRhOiBZaWVsZCB2cy4gb3RoZXIgdmFyaWFibGVzIiwjPC0gYWRkIHRpdGxlIGFuZCBzdWJ0aXRsZQogICAgICAgc3VidGl0bGUgPSAiMkQgZGlzdHJpYnV0aW9uIG9mIHNjYWxlZCBkYXRhIikKCiMgVmlldyB1cGRhdGVkIHBsb3QuCnNjYXR0ZXJfbm9ybQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDQwOiBTYXZpbmcgcGxvdHMgaW4gUjogUE5HIGV4cG9ydGVkICAjIyMjCgojIFNldCB3b3JraW5nIGRpcmVjdG9yeSAKIyB0byB3aGVyZSB3ZSB3YW50IHRvIHNhdmUgb3VyIHBsb3RzLgoKYGBge3J9CnNldHdkKHBsb3RfZGlyKQoKcG5nKCJDTVBfYm94cGxvdHNfbm9ybS5wbmciLAogICAgd2lkdGggPSAxMjAwLCAKICAgIGhlaWdodCA9IDYwMCwgCiAgICB1bml0cyA9ICJweCIpCmJveHBsb3RzX25vcm0KZGV2Lm9mZigpCgpwbmcoIkNNUF9kZW5zaXR5X25vcm0ucG5nIiwKICAgIHdpZHRoID0gMTIwMCwgCiAgICBoZWlnaHQgPSA2MDAsIAogICAgdW5pdHMgPSAicHgiKQpkZW5zaXR5X25vcm0KZGV2Lm9mZigpCgpwbmcoIkNNUF9zY2F0dGVycGxvdF9ub3JtLnBuZyIsCiAgICB3aWR0aCA9IDEyMDAsIAogICAgaGVpZ2h0ID0gNjAwLCAKICAgIHVuaXRzID0gInB4IikKc2NhdHRlcl9ub3JtCmRldi5vZmYoKQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDQzOiBTYXZpbmcgcGxvdHMgaW4gUjogUERGIGV4cG9ydGVkICAjIyMjCgoKCmBgYHtyfQojIFNldCB3b3JraW5nIGRpcmVjdG9yeSB0byB3aGVyZSB5b3Ugd2FudCB0byBzYXZlIHBsb3RzLgpzZXR3ZChwbG90X2RpcikKcGRmKCJDTVBfcGxvdHMucGRmIiwgIzwtIG5hbWUgb2YgZmlsZQogICAgd2lkdGggPSAxNiwgICAgICAjPC0gd2lkdGggaW4gaW5jaGVzCiAgICBoZWlnaHQgPSA4KSAgICAgICM8LSBoZWlnaHQgLi4uCgpib3hwbG90c19ub3JtICAgICAgICAjPC0gcGxvdCAxCmRlbnNpdHlfbm9ybSAgICAgICAgICM8LSBwbG90IDIKc2NhdHRlcl9ub3JtICAgICAgICAgIzwtIHBsb3QgMwoKZGV2Lm9mZigpICAgICAgICAgICAgIzwtIGNsZWFyIGdyYXBoaWNzIGRldmljZQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDQ1OiBFeGVyY2lzZSAyICAjIyMjCgoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSA0NzogSW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbnM6IGhpZ2hjaGFydGVyICAjIyMjCgpgYGB7cn0KIyBJbnN0YWxsIGBoaWdoY2hhcnRlcmAgcGFja2FnZS4KaW5zdGFsbC5wYWNrYWdlcygiaGlnaGNoYXJ0ZXIiKQoKIyBMb2FkIHRoZSBsaWJyYXJ5LgpsaWJyYXJ5KGhpZ2hjaGFydGVyKQoKIyBWaWV3IGRvY3VtZW50YXRpb24uCmxpYnJhcnkoaGVscCA9ICJoaWdoY2hhcnRlciIpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgNTU6IEhpZ2hjaGFydHMgZ2VuZXJpYyBmdW5jdGlvbiBoY2hhcnQ6IHNjYXR0ZXIgICMjIyMKCmBgYHtyfQoKIyBDb25zdHJ1Y3QgYW4gaW50ZXJhY3RpdmUgc2NhdHRlcnBsb3QuCnNjYXR0ZXJfaW50ZXJhY3RpdmUgPSAgICAgICAgICAgICAgIzwtIG5hbWUgdGhlIHBsb3QgICAKICBoY2hhcnQoQ01QX3N1YnNldF9sb25nMiwgICAgICAgICAjPC0gc2V0IGRhdGEKICAgICAgICAgInNjYXR0ZXIiLCAgICAgICAgICAgICAgICAjPC0gbWFrZSB0eXBlICJzY2F0dGVyIgogICAgICAgICAgaGNhZXMoeCA9IG5vcm1fdmFsdWUsICAgICM8LSBtYXAgeC1heGlzCiAgICAgICAgICAgICAgICB5ID0gWWllbGQsICAgICAgICAgIzwtIG1hcCB5LWF4aXMKICAgICAgICAgICAgICAgIGdyb3VwID0gdmFyaWFibGUpKSAjPC0gZ3JvdXAgYnkKCmBgYAoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSA1NjogSGlnaGNoYXJ0cyBnZW5lcmljIGZ1bmN0aW9uIGhjaGFydDogc2NhdHRlciAgIyMjIwoKYGBge3J9CnNjYXR0ZXJfaW50ZXJhY3RpdmUKYGBgCgoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSA1ODogSGlnaGNoYXJ0cyBnZW5lcmljIGZ1bmN0aW9uIGhjaGFydDogc2NhdHRlciAgIyMjIwoKYGBge3J9CiMgUGlwZSBjaGFydCBvcHRpb25zIHRvIG9yaWdpbmFsIGNoYXJ0LgpzY2F0dGVyX2ludGVyYWN0aXZlID0gc2NhdHRlcl9pbnRlcmFjdGl2ZSAlPiUKICAjIFVzZSBjaGFydCBvcHRpb25zIHRvIHNwZWNpZnkgem9vbS4KICBoY19jaGFydCh6b29tVHlwZSA9ICJ4eSIpIAoKc2NhdHRlcl9pbnRlcmFjdGl2ZQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDU5OiBIaWdoY2hhcnRzIGdlbmVyaWMgZnVuY3Rpb24gaGNoYXJ0OiBzY2F0dGVyICAjIyMjCgpgYGB7cn0KIyBQaXBlIGNoYXJ0IG9wdGlvbnMgdG8gb3JpZ2luYWwgY2hhcnQuCnNjYXR0ZXJfaW50ZXJhY3RpdmUgPSBzY2F0dGVyX2ludGVyYWN0aXZlICU+JQogIyBBZGQgdGl0bGUgdG8geW91ciBwbG90LgogaGNfdGl0bGUodGV4dCA9ICJDTVAgZGF0YTogWWllbGQgdnMuIG90aGVyIHZhcmlhYmxlcyIpCgpzY2F0dGVyX2ludGVyYWN0aXZlCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgNjA6IENvcnJlbGF0aW9uIG1hdHJpeCB3aXRoIGhjaGFydCAgIyMjIwoKYGBge3J9CiMgQ29tcHV0ZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCBmb3IgdGhlIGZpcnN0IAojIDQgdmFyaWFibGVzIGluIG91ciBkYXRhLgpjb3JfbWF0cml4ID0gY29yKENNUF9zdWJzZXRbLCAxOjRdKQpgYGAKCgpgYGB7cn0KIyBDb25zdHJ1Y3QgYSBjb3JyZWxhdGlvbiBwbG90IGJ5IAojIHNpbXBseSBnaXZpbmcgdGhlIHBsb3R0aW5nIGZ1bmN0aW9uCiMgYSBjb3JyZWxhdGlvbiBtYXRyaXguCmNvcnJlbGF0aW9uX2ludGVyYWN0aXZlID0gaGNoYXJ0KGNvcl9tYXRyaXgpICU+JQogIyBBZGQgdGl0bGUgdG8geW91ciBwbG90LgogaGNfdGl0bGUodGV4dCA9ICJDTVAgZGF0YTogY29ycmVsYXRpb24iKQoKY29ycmVsYXRpb25faW50ZXJhY3RpdmUKYGBgCgoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSA2MjogRXhlcmNpc2UgMyAgIyMjIwoKCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgNjU6IFN1bW1hcnkgY29sdW1uIHBsb3Qgd2l0aCBoY2hhcnQgICMjIyMKCmBgYHtyfQojIENyZWF0ZSBkYXRhIHN1bW1hcnkuCkNNUF9zdW1tYXJ5ID0gc3VtbWFyeShDTVBfc3Vic2V0KQoKIyBTYXZlIGl0IGFzIGEgZGF0YWZyYW1lLgpDTVBfc3VtbWFyeSA9IGFzLmRhdGEuZnJhbWUoQ01QX3N1bW1hcnkpCgojIEluc3BlY3QgdGhlIGRhdGEuCmhlYWQoQ01QX3N1bW1hcnkpCiMgUmVtb3ZlIGFuIGVtcHR5IHZhcmlhYmxlLgpDTVBfc3VtbWFyeSRWYXIxID0gTlVMTAoKIyBSZW5hbWUgcmVtYWluaW5nIGNvbHVtbnMuCmNvbG5hbWVzKENNUF9zdW1tYXJ5KSA9IGMoIlZhcmlhYmxlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlN1bW1hcnkiKQojIEluc3BlY3QgdXBkYXRlZCBkYXRhLgpoZWFkKENNUF9zdW1tYXJ5KQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDY2OiBTdW1tYXJ5IGNvbHVtbiBwbG90IHdpdGggaGNoYXJ0ICAjIyMjCgpgYGB7cn0KIyBTZXBhcmF0ZSBgU3VtbWFyeWAgY29sdW1uIGludG8gMiBjb2x1bW5zLgpDTVBfc3VtbWFyeSA9IENNUF9zdW1tYXJ5ICU+JSAgICAgICAgICAgICAgIzwtIHNldCBvcmlnaW5hbCBkYXRhIAogIHNlcGFyYXRlKFN1bW1hcnksICAgICAgICAgICAgICAgICAgICAgICAgIzwtIHNlcGFyYXRlIGBTdW1tYXJ5YCB2YXJpYWJsZSAKICAgICAgICAgICBpbnRvID0gYygiU3RhdGlzdGljIiwgIlZhbHVlIiksICM8LSBpbnRvIDIgY29sdW1uczogYFN0YXRpc3RpY2AsIGBWYWx1ZWAKICAgICAgICAgICBzZXAgPSAiOiIsICAgICAgICAgICAgICAgICAgICAgICM8LSBzZXQgc2VwYXJhdGluZyBjaGFyYWN0ZXIKICAgICAgICAgICBjb252ZXJ0ID0gVFJVRSkgICAgICAgICAgICAgICAgICM8LSB3aGVyZSBhcHBsaWNhYmxlIGNvbnZlcnQgZGF0YSAodG8gbnVtZXJpYykKCmBgYAoKCmBgYHtyfQojIEluc3BlY3QgdGhlIGZpcnN0IGZldyBlbnRyaWVzIGluIHRoZSBkYXRhLgpoZWFkKENNUF9zdW1tYXJ5KQoKIyBJbnNwZWN0IHRvdGFsIG51bWJlciBvZiByb3dzIGluIGRhdGEgaW5jbHVkaW5nIE5Bcy4KbnJvdyhDTVBfc3VtbWFyeSkKYGBgCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDY3OiBTdW1tYXJ5IGNvbHVtbiBwbG90IHdpdGggaGNoYXJ0ICAjIyMjCgpgYGB7cn0KIyBJbnNwZWN0IGBWYWx1ZWAgY29sdW1uIGZvciBgTkFzYC4Kd2hpY2goaXMubmEoQ01QX3N1bW1hcnkkVmFsdWUpID09IFRSVUUpCmBgYAoKCmBgYHtyfQojIFN1YnNldCBvbmx5IHJvd3Mgd2hlcmUgYFZhbHVlYCBpcyBub3QgTkFzLgpDTVBfc3VtbWFyeSA9IHN1YnNldChDTVBfc3VtbWFyeSwgIWlzLm5hKFZhbHVlKSkKYGBgCgoKYGBge3J9CiMgTm93IHRoZSBudW1iZXIgb2Ygcm93cyBzaG91bGQgYmUgNCBsZXNzLgpucm93KENNUF9zdW1tYXJ5KQpgYGAKCgpgYGB7cn0KIyBDb25zdHJ1Y3QgdGhlIHN1bW1hcnkgY2hhcnQuCkNNUF9zdW1tYXJ5X2ludGVyYWN0aXZlID0gCiAgaGNoYXJ0KENNUF9zdW1tYXJ5LCAgICAgICAgICAgICAjPC0gc2V0IGRhdGEKICAgICAgICAgImNvbHVtbiIsICAgICAgICAgICAgICAgICM8LSBzZXQgdHlwZSAoYGNvbHVtbmAgaW4gaGlnaGNoYXJ0cykKICAgICAgICAgaGNhZXMoeCA9IFN0YXRpc3RpYywgICAgICM8LSBhcnJhbmdlIGBTdGF0aXN0aWNzYCBhY3Jvc3MgeC1heGlzCiAgICAgICAgICAgICAgIHkgPSBWYWx1ZSwgICAgICAgICAjPC0gbWFwIGBWYWx1ZWAgb2YgZWFjaCBgU3RhdGlzdGljYCB0byB5LWF4aXMKICAgICAgICAgICAgICAgZ3JvdXAgPSBWYXJpYWJsZSkpICM8LSBncm91cCBjb2x1bW5zIGJ5IGBWYXJpYWJsZWAKYGBgCgoKCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgNjg6IFN1bW1hcnkgY29sdW1uIHBsb3Qgd2l0aCBoY2hhcnQgICMjIyMKCmBgYHtyfQpDTVBfc3VtbWFyeV9pbnRlcmFjdGl2ZQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDY5OiBTdW1tYXJ5IGNvbHVtbiBwbG90IHdpdGggaGNoYXJ0ICAjIyMjCgoKYGBge3J9CiMgQWRqdXN0IHRvb2x0aXAgb3B0aW9ucyBieSBwaXBpbmcgYGhjX3Rvb2x0aXBgIHRvIGJhc2UgcGxvdC4KQ01QX3N1bW1hcnlfaW50ZXJhY3RpdmUgPSBDTVBfc3VtbWFyeV9pbnRlcmFjdGl2ZSAlPiUgCiAgaGNfdG9vbHRpcChzaGFyZWQgPSBUUlVFKSAgJT4lICAgICAgICAgICAgICAgIzwtIGBzaGFyZWRgIG5lZWRzIHRvIGJlIHNldCB0byBgVFJVRWAKICBoY190aXRsZSh0ZXh0ID0gIkNNUCBkYXRhIHZhcmlhYmxlIHN1bW1hcnkiKSAjPC0gYWRkIHRpdGxlIHRvIHlvdXIgcGxvdAoKYGBgCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgNzA6IFN1bW1hcnkgY29sdW1uIHBsb3Qgd2l0aCBoY2hhcnQgICMjIyMKCmBgYHtyfQpDTVBfc3VtbWFyeV9pbnRlcmFjdGl2ZQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDczOiBIaWdoY2hhcnRzIGJveHBsb3Q6IGhjYm94cGxvdCAgIyMjIwoKYGBge3J9CiMgQ29uc3RydWN0IGFuIGludGVyYWN0aXZlIGJveHBsb3QuCkNNUF9zdWJzZXRfbG9uZwpib3hwbG90X2ludGVyYWN0aXZlID0gIAogIGhjYm94cGxvdCh4ID0gQ01QX3N1YnNldF9sb25nJG5vcm1fdmFsdWUsCiAgICAgICAgICAgIHZhciA9IENNUF9zdWJzZXRfbG9uZyR2YXJpYWJsZSwKICAgICAgICAgICAgbmFtZSA9ICJOb3JtYWxpemVkIHZhbHVlIikgJT4lCiAgaGNfdGl0bGUodGV4dCA9ICJDTVAgZGF0YSB2YXJpYWJsZXMiKQpib3hwbG90X2ludGVyYWN0aXZlCmBgYAoKCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0tCiMjIyMgU2xpZGUgNzY6IEhpZ2hjaGFydHMgYm94cGxvdDogaGNib3hwbG90ICAjIyMjCgpgYGB7cn0KIyBFbmhhbmNlIG9yaWdpbmFsIGJveHBsb3Qgd2l0aCBzb21lIG9wdGlvbnMuCmJveHBsb3RfaW50ZXJhY3RpdmUgPSBib3hwbG90X2ludGVyYWN0aXZlICU+JSAKICBoY19wbG90T3B0aW9ucyggICAgICAgIzwtIHBsb3Qgb3B0aW9ucwogICAgYm94cGxvdCA9IGxpc3QoICAgICAjPC0gZm9yIGJveHBsb3QgCiAgICAgIGNvbG9yQnlQb2ludCA9IFRSVUUpKSAjPC0gY29sb3IgZWFjaCBib3gKYm94cGxvdF9pbnRlcmFjdGl2ZQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDc5OiBDb21wb3VuZCBwbG90czogZGVuc2l0eSArIGxpbmVzIGV4YW1wbGUgICMjIyMKCmBgYHtyfQpsYXllcmVkX2RlbnNpdHlfaW50ZXJhY3RpdmUgPSBoaWdoY2hhcnQoKSAlPiUgI21haW4gY2hhcnQKICBoY19jaGFydCh0eXBlID0gImFyZWEiKSAlPiUgI2dsb2JhbCBjaGFydCBvcHRpb24gZm9yIHRvIGFwcGx5IGZvciBhbGwgbGF5ZXJzCiAgaGNfYWRkX3NlcmllcyhkYXRhID0gZGVuc2l0eShDTVBfc3Vic2V0JFlpZWxkKSwKICAgICAgICAgICAgICAgIG5hbWUgPSAiWWllbGQiKSAlPiUKICBoY19hZGRfc2VyaWVzKGRhdGEgPSBkZW5zaXR5KENNUF9zdWJzZXQkQmlvbG9naWNhbE1hdGVyaWFsMDEpLAogICAgICAgICAgICAgICAgbmFtZSA9ICJCaW8gTWF0ZXJpYWwgMSIpICU+JQogIGhjX2FkZF9zZXJpZXMoZGF0YSA9IGRlbnNpdHkoQ01QX3N1YnNldCRNYW51ZmFjdHVyaW5nUHJvY2VzczAxLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgbmFtZSA9ICJNYW4uIFByb2Nlc3MgMSIpICU+JQogIGhjX3hBeGlzKHBsb3RMaW5lcyA9IGxpc3QoCiAgICAgICAgICAgIGxpc3QobGFiZWwgPSBsaXN0KHRleHQgPSAiWWllbGQgYXZnLiIpLAogICAgICAgICAgICAgICAgICB3aWR0aCA9IDIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gbWVhbihDTVBfc3Vic2V0JFlpZWxkKSksCiAgICAgICAgICAgIGxpc3QobGFiZWwgPSBsaXN0KHRleHQgPSAiQmlvIE1hdGVyaWFsIDEgYXZnLiIpLAogICAgICAgICAgICAgICAgICB3aWR0aCA9IDIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gbWVhbihDTVBfc3Vic2V0JEJpb2xvZ2ljYWxNYXRlcmlhbDAxKSksCiAgICAgICAgICAgIGxpc3QobGFiZWwgPSBsaXN0KHRleHQgPSAiTWFuLiBQcm9jZXNzIDEgYXZnLiIpLAogICAgICAgICAgICAgICAgICB3aWR0aCA9IDIsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gbWVhbihDTVBfc3Vic2V0JE1hbnVmYWN0dXJpbmdQcm9jZXNzMDEsIG5hLnJtID0gVFJVRSkpKSkgJT4lCiAgaGNfdG9vbHRpcChjcm9zc2hhaXJzID0gVFJVRSkgJT4lCiAgaGNfdGl0bGUodGV4dCA9ICJDTVAgZGF0YTogZGVuc2l0eSBhbmQgYXZlcmFnZSBvZiBzZWxlY3QgdmFyaWFibGVzIikKYGBgCgoKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PS0KIyMjIyBTbGlkZSA4MDogQ29tcG91bmQgcGxvdHM6IGhpZ2hjaGFydCB3aXRoIGxheWVycyAgIyMjIwoKYGBge3J9CmxheWVyZWRfZGVuc2l0eV9pbnRlcmFjdGl2ZQpgYGAKCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09LQojIyMjIFNsaWRlIDgzOiBFeGVyY2lzZSA0ICAjIyMjCgoKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMgIENPTkdSQVRVTEFUSU9OUyBPTiBDT01QTEVUSU5HIFRISVMgTU9EVUxFISAgICMjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwo=